from Envs import Envs
from object import AtomicObject

debug = False

class Tree:
	def __init__(self, type = "undefined", value = None, ltree = None, rtree = None):
		self.type = type
		self.value = value
		self.ltree = ltree
		self.rtree = rtree
		self.isLeaf = (ltree == None and rtree == None)

	def __repr__(self):
		toReturn = " {"
		if self.ltree != None:
			toReturn += self.ltree.__repr__()
		toReturn += ' ' + self.type + ' '
		if self.rtree != None:
			toReturn += self.rtree.__repr__()
		if self.isLeaf:
			return "LEAF(" + str(self.value) + ")"
#		return "TREE(" + str(self.value) + ")"
		return toReturn + "} "
	
	
	def eval(self):
		# sprobuje zrobic leniwa ewaluacje 
		# najpierw lewa strona zapytania pozniej jezeli trzeba prawa
		t = self.type
		######################
		######  ASSIGN  ######
		######################
		if t == "assign":
			name = self.ltree.value
			valueType, value = self.rtree.eval()
			self.envs.setVar(name, value)
			if debug: print len(self.envs.ids)
			return (valueType, value)
		
		
		if self.ltree != None:
			ltype, lt = self.ltree.eval()
#		if self.rtree != None:
#			rtype, rt = self.rtree.eval()

		
		
		######################
		#######  NAME  #######
		######################
		if t == "name":
			bindedObjects = self.envs.bind(self.value)
			if debug: print bindedObjects
			if bindedObjects == None:
				return "syntax", "syntax"
			return "objectList", bindedObjects
		
		#####################
		#######  DOT  #######
		#####################
		elif t == "dot":
			objectList = []
			if ltype == "objectList":
				for lObj in lt:
					self.envs.nested(lObj)
					# jezeli mamy po kropce tylko nazwe
					if self.rtree.type == "name":
						objList = self.envs.bind(self.rtree.value, 1)
						if objList != None:
							objectList.extend(objList)
					# jezeli po kropce jest lista obiektow
					else:
						rtype, rt = self.rtree.eval()
						if rtype == "nameList":
#							if debug: print self.envs.levels[-1]
							for rObj in rt:
								objList = self.envs.bind(rObj.value, 1)
								if objList != None:
									objectList.extend(objList)
					self.envs.close()
				return ("objectList", objectList)
		
		#######################
		#######  COMMA  #######
		#######################
		elif t == "comma":
			
			#print "comma", self.ltree.type, self.rtree.type
			if self.ltree.type == "name" and self.rtree.type == "name":
				return ("nameList", [self.ltree, self.rtree])
			if self.ltree.type == "comma" and self.rtree.type == "name":# or self.rtree.type == "name"):
				ltype, lt = self.ltree.eval()
				lt.append(self.rtree)
				return ("nameList", lt)
		
		elif t == "nameList":
#			print "nameList", self.value
			objectList = []
			for obj in self.value:
				type, value = obj.eval()
				objectList.extend(value)
			return ("objectList", objectList)
		
		#######################
		#######  WHERE  #######
		#######################
		elif t == "where":
			if ltype == "objectList":
				objectList = list(lt)
				for lObj in lt:
					self.envs.nested(lObj)
					rtype, rt = self.rtree.eval()
					#if debug: print "rtype, rt", rtype, rt
					if rtype == "bool" and not rt:
						objectList.remove(lObj)
					self.envs.close()
				return ("objectList", objectList)
		
		##############################
		####### REL OPERATORS  #######
		##############################
		elif t == ">" or t == "<" or t == ">=" or t == "<=" or t == "==" or t == "!=":
			rtype , rt = self.rtree.eval()
			if t == ">":
				if ltype == "number" and rtype == "number":
					return ("bool", (lt > rt))
				elif  ltype == "objectList" and (rtype == "string" or rtype == "number"):
					toReturnBool = True
					for lObj in lt:
						#if debug: print "lObj.value", lObj.value
						toReturnBool = toReturnBool and lObj.value > rt
					return ("bool", toReturnBool)
			elif t == "<":
				if ltype == "number" and rtype == "number":
					return ("bool", (lt < rt))
				elif  ltype == "objectList" and (rtype == "string" or rtype == "number"):
					toReturnBool = True
					for lObj in lt:
						#if debug: print "lObj.value", lObj.value
						toReturnBool = toReturnBool and lObj.value < rt
					return ("bool", toReturnBool)
			elif t == ">=":
				if ltype == "number" and rtype == "number":
					return ("bool", (lt >= rt))
				elif  ltype == "objectList" and (rtype == "string" or rtype == "number"):
					toReturnBool = True
					for lObj in lt:
						#if debug: print "lObj.value", lObj.value
						toReturnBool = toReturnBool and lObj.value >= rt
					return ("bool", toReturnBool)
			elif t == "<=":
				if ltype == "number" and rtype == "number":
					return ("bool", (lt <= rt))
				elif  ltype == "objectList" and (rtype == "string" or rtype == "number"):
					toReturnBool = True
					for lObj in lt:
						#if debug: print "lObj.value", lObj.value
						toReturnBool = toReturnBool and lObj.value <= rt
					return ("bool", toReturnBool)
			elif t == "==":
				# EQUALS
				if ltype == "number" and rtype == "number":
					return ("bool", (lt == rt))
				elif  ltype == "objectList" and (rtype == "string" or rtype == "number"):
					toReturnBool = True
					for lObj in lt:
						#if debug: print "lObj.value", lObj.value
						toReturnBool = toReturnBool and lObj.value == rt
					return ("bool", toReturnBool)
				elif ltype == "objectList" and rtype == "objectList" and len(rt) == 1 and rt[0].type == "atomic":
					toReturnBool = True
					for lObj in lt:
						toReturnBool = toReturnBool and lObj.value == rt[0].value
					return ("bool", toReturnBool)
			elif t == "!=":
				if ltype == "number" and rtype == "number":
					return ("bool", (lt != rt))
				elif  ltype == "objectList" and (rtype == "string" or rtype == "number"):
					toReturnBool = True
					for lObj in lt:
						#if debug: print "lObj.value", lObj.value
						toReturnBool = toReturnBool and lObj.value != rt
					return ("bool", toReturnBool)
			
			return ("bool", False)
		##############################
		#### ARITMETIC OPERATORS  ####
		##############################
		elif t == "+" or t == "-" or t == "*" or t == "/" or t == "%" or t == "**":
			rtype , rt = self.rtree.eval()
			if ltype == "number" and rtype == "number":
				if t == "+":
					return ("number", lt + rt)
				elif t == "-":
					return ("number", lt - rt)
				elif t == "*":
					return ("number", lt * rt)
				elif t == "/":
					return ("number", lt / rt)
				elif t == "%":
					return ("number", lt % rt)
				elif t == "**":
					return ("number", lt ** rt)
			elif ltype == "objectList":
				if rtype == "number":
					if len(lt) == 1 and lt[0].type == "atomic":
						if t == "+":
							return ("number", lt[0].value + rt)
						elif t == "-":
							return ("number", lt[0].value - rt)
						elif t == "*":
							return ("number", lt[0].value * rt)
						elif t == "/":
							return ("number", lt[0].value / rt)
						elif t == "%":
							return ("number", lt[0].value % rt)
						elif t == "**":
							return ("number", lt[0].value ** rt)
					else:
						return ("syntax", "syntax")
				elif rtype == "objectList" and len(rt) == 1 and rt[0].type == "atomic":
					if t == "+":
						return ("number", lt[0].value + rt[0].value)
					elif t == "-":
						return ("number", lt[0].value - rt[0].value)
					elif t == "*":
						return ("number", lt[0].value * rt[0].value)
					elif t == "/":
						return ("number", lt[0].value / rt[0].value)
					elif t == "%":
						return ("number", lt[0].value % rt[0].value)
					elif t == "**":
						return ("number", lt[0].value ** rt[0].value)
			else:
				return ("syntax", "syntax")
			
		
		elif t == "number":
			return ("number", self.value)
		
		elif t == "string":
			return ("string", self.value)
		
#		return "None", None
		return "syntax", "Syntax error"